package com.skyline.energy.provider.spring;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.PreparedStatementCreator;

import com.skyline.energy.dataaccess.dialect.Dialect;
import com.skyline.energy.dataaccess.jdbc.JdbcDataAccessor;
import com.skyline.energy.dataaccess.jdbc.KeyHolder;
import com.skyline.energy.dataaccess.jdbc.RowMapper;
import com.skyline.energy.utils.Page;

public class SpringDataAccessor implements JdbcDataAccessor {
	private static final Log LOGGER = LogFactory.getLog(SpringDataAccessor.class);
	private JdbcTemplate jdbcTemplate;
	private Dialect dialect;

	public void setDialect(Dialect dialect) {
		this.dialect = dialect;
	}

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	@Override
	public <T> List<T> query(String sql, RowMapper<T> rowMapper, int fetchSize, Object... args) {
		jdbcTemplate.setFetchSize(fetchSize);
		return jdbcTemplate.query(sql, args, getWrapperRowMapper(rowMapper));
	}

	@Override
	public int update(String sql, KeyHolder keyHolder, Object... args) {
		boolean returnKeys = (keyHolder != null);
		PreparedStatementCreator psc = getPreparedStatementCreator(sql, args, returnKeys);
		if (keyHolder == null) {
			return jdbcTemplate.update(psc);
		} else {
			return jdbcTemplate.update(psc, getWrapperKeyHolder(keyHolder));
		}
	}

	@Override
	public int[] batchUpdate(String sql, List<Object[]> argsList, KeyHolder generatedKeyHolder) {
		BatchPreparedStatementSetter pss = getBatchPreparedStatementSetter(argsList);
		PreparedStatementCallback<int[]> action = getPreparedStatementCallback(pss, generatedKeyHolder);
		boolean returnKeys = (generatedKeyHolder != null);
		PreparedStatementCreator psc = getPreparedStatementCreator(sql, returnKeys);
		return jdbcTemplate.execute(psc, action);
	}

	@Override
	public int queryCount(String sql, Object... args) {
		String countSql = dialect.getCountSql(sql);

		LOGGER.info("Count total page size SQL:" + countSql);

		return jdbcTemplate.queryForInt(countSql, args);
	}

	@Override
	public <T> List<T> queryPage(String sql, Page page, RowMapper<T> rowMapper, int fetchSize, Object... args) {
		jdbcTemplate.setFetchSize(fetchSize);

		int total = queryCount(sql, args);

		page.setTotal(total);
		if (total <= 0) {
			return new ArrayList<T>(0);
		}
		String pageSql = dialect.getPaginationSql(sql, page);

		LOGGER.info("Pagination Query SQL:" + pageSql);

		return jdbcTemplate.query(pageSql, getWrapperRowMapper(rowMapper), args);
	}

	@Override
	public KeyHolder getKeyHolder() {
		return new SpringKeyHolder();
	}
	
	private PreparedStatementCreator getPreparedStatementCreator(String sql, Object[] args, boolean returnKeys) {
		return PreparedStatementCreatorFactory.createPreparedStatementCreator(sql, args, returnKeys);
	}
	
	private PreparedStatementCreator getPreparedStatementCreator(String sql, boolean returnKeys) {
		return PreparedStatementCreatorFactory.createPreparedStatementCreator(sql, returnKeys);
	}
	
	private PreparedStatementCallback<int[]> getPreparedStatementCallback(BatchPreparedStatementSetter pss,
			KeyHolder generatedKeyHolder) {
		return PreparedStatementCallbackCreator.createPreparedStatementCallback(pss, generatedKeyHolder);
	}

	private BatchPreparedStatementSetter getBatchPreparedStatementSetter(List<Object[]> argsList) {
		return BatchPreparedStatementSetterCreator.createBatchPreparedStatementSetter(argsList);
	}

	private <T> org.springframework.jdbc.core.RowMapper<T> getWrapperRowMapper(RowMapper<T> mapper) {
		return new SpringRowMapperAdapter<T>(mapper);
	}

	private org.springframework.jdbc.support.KeyHolder getWrapperKeyHolder(KeyHolder keyHolder) {
		if (keyHolder == null) {
			return null;
		}
		return new SpringKeyHolderAdapter(keyHolder);
	}

}
